package com.ruoyi.framework.sso;

import java.util.Objects;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestTemplate;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.config.properties.UserCenterProperties;
import com.ruoyi.framework.sso.domain.UserCenterTokenResponse;
import com.ruoyi.framework.sso.domain.UserCenterUserInfo;

/**
 * 用户中心接口客户端
 */
@Component
public class UserCenterClient
{
    private static final Logger log = LoggerFactory.getLogger(UserCenterClient.class);

    private static final String TOKEN_PATH = "/v1/authorize/%s/oauth2/token";
    private static final String USER_INFO_PATH = "/v1/authorize/%s/oauth2/userinfo";
    private static final String LOGOUT_PATH = "/v1/authorize/%s/user/logout";

    private final RestTemplate restTemplate;
    private final UserCenterProperties properties;

    public UserCenterClient(RestTemplate restTemplate, UserCenterProperties properties)
    {
        this.restTemplate = restTemplate;
        this.properties = properties;
    }

    /**
     * 使用授权码换取 access_token
     */
    public UserCenterTokenResponse exchangeByCode(String code, String redirectUri)
    {
        MultiValueMap<String, String> body = baseForm();
        body.add("grant_type", "authorization_code");
        body.add("code", code);
        body.add("redirect_uri", getRedirectUri(redirectUri));
        log.info("body：{}", JSONObject.toJSONString(body));
        return postForToken(body);
    }

    /**
     * 使用 refresh_token 刷新 access_token
     */
    public UserCenterTokenResponse refresh(String refreshToken)
    {
        MultiValueMap<String, String> body = baseForm();
        body.add("grant_type", "refresh_token");
        body.add("refresh_token", refreshToken);
        return postForToken(body);
    }

    /**
     * 查询用户中心用户信息
     */
    public UserCenterUserInfo getUserInfo(String accessToken)
    {
        HttpHeaders headers = new HttpHeaders();
        headers.add(HttpHeaders.AUTHORIZATION, buildBearer(accessToken));
        try
        {
            ResponseEntity<UserCenterUserInfo> entity = restTemplate.exchange(
                buildUrl(USER_INFO_PATH), HttpMethod.GET, new HttpEntity<>(headers), UserCenterUserInfo.class);
            UserCenterUserInfo body = entity.getBody();
            if (body == null)
            {
                throw new ServiceException("用户中心接口返回为空");
            }
            return body;
        }
        catch (HttpStatusCodeException ex)
        {
            log.error("调用用户中心查询用户信息失败：{}", ex.getResponseBodyAsString());
            throw asServiceException(ex);
        }
    }

    /**
     * 通知用户中心退出
     */
    public void logout(String accessToken)
    {
        HttpHeaders headers = new HttpHeaders();
        headers.add(HttpHeaders.AUTHORIZATION, buildBearer(accessToken));
        try
        {
            restTemplate.exchange(buildUrl(LOGOUT_PATH), HttpMethod.GET, new HttpEntity<>(headers), Void.class);
        }
        catch (HttpStatusCodeException ex)
        {
            log.warn("调用用户中心退出接口失败：{}", ex.getResponseBodyAsString());
        }
    }

    private UserCenterTokenResponse postForToken(MultiValueMap<String, String> body)
    {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        try
        {
            ResponseEntity<UserCenterTokenResponse> entity = restTemplate.postForEntity(
                    buildUrl(TOKEN_PATH), new HttpEntity<>(body, headers), UserCenterTokenResponse.class);
            UserCenterTokenResponse response = entity.getBody();
            if (response == null || StringUtils.isEmpty(response.getAccessToken()))
            {
                throw new ServiceException("用户中心 token 接口返回为空");
            }
            return response;

           /* String url = buildUrl(TOKEN_PATH);
            log.info("url：{}", url);
            log.info("headers：{}", JSONObject.toJSONString(headers));
            log.info("body：{}", JSONObject.toJSONString(body));
            HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(body, headers);
            ResponseEntity<String> response = restTemplate.exchange(buildUrl(TOKEN_PATH), HttpMethod.POST,
                    requestEntity, String.class);

            log.info("response：{}", response.getBody());

            JSONObject tokenObject = JSON.parseObject(response.getBody());
            UserCenterTokenResponse userCenterTokenResponse = new UserCenterTokenResponse();
            userCenterTokenResponse.setAccessToken(tokenObject.getString("access_token"));
            userCenterTokenResponse.setRefreshToken(tokenObject.getString("refresh_token"));
            userCenterTokenResponse.setExpiresIn(tokenObject.getLong("expires_in"));
            userCenterTokenResponse.setRefreshTokenExpiresIn(tokenObject.getLong("refresh_token_expires_in"));

            if (response == null || StringUtils.isEmpty(userCenterTokenResponse.getAccessToken()))
            {
                throw new ServiceException("用户中心 token 接口返回为空");
            }*/
        }
        catch (HttpStatusCodeException ex)
        {
            log.error("调用用户中心 token 接口失败：{}", ex.getResponseBodyAsString());
            throw asServiceException(ex);
        }
    }

    private MultiValueMap<String, String> baseForm()
    {
        MultiValueMap<String, String> form = new LinkedMultiValueMap<>();
        form.add("client_id", properties.getClientId());
        form.add("client_secret", properties.getClientSecret());
        return form;
    }

    private String buildUrl(String pathTemplate)
    {
        String base = StringUtils.trimToEmpty(properties.getServerUrl());
        String path = String.format(pathTemplate, properties.getClientCode());
        if (base.endsWith("/"))
        {
            base = base.substring(0, base.length() - 1);
        }
        if (base.endsWith("/api") && path.startsWith("/api/"))
        {
            // 避免出现 /api/api/ 重复路径
            path = path.substring(4);
        }
        return base + path;
    }

    private String buildBearer(String token)
    {
        if (StringUtils.isBlank(token))
        {
            return "";
        }
        return token.startsWith("Bearer") ? token : "Bearer " + token;
    }

    private ServiceException asServiceException(HttpStatusCodeException ex)
    {
        String message = ex.getResponseBodyAsString();
        if (StringUtils.isBlank(message))
        {
            message = ex.getStatusText();
        }
        return new ServiceException(String.format("用户中心接口调用失败：%s", message));
    }

    private String getRedirectUri(String redirectUri)
    {
        return StringUtils.defaultIfBlank(redirectUri, properties.getRedirectUri());
    }
}
